package com.service.business.merchant.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beust.jcommander.internal.Lists;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.service.business.base.dto.LoginUserSession;
import com.service.business.base.service.CoinsPriceService;
import com.service.business.base.service.ConfigService;
import com.service.business.member.dto.MemberStoreChange;
import com.service.business.member.service.*;
import com.service.business.merchant.controller.rep.MerchantOrderPagRep;
import com.service.business.merchant.controller.req.MerchantOrderCreateReq;
import com.service.business.merchant.controller.req.MerchantOrderPageReq;
import com.service.business.merchant.service.*;
import com.service.business.merchant.support.WriteOffOrder;
import com.service.business.rabbitmq.binding.WriteOffOrderSendHandler;
import com.service.business.support.SessionUtil;
import com.service.core.exception.CommonException;
import com.service.core.rest.response.CommonPageRep;
import com.service.dao.constants.ConfigConstants;
import com.service.dao.dto.MerchantCreditLineChange;
import com.service.dao.enums.CoinsEnum;
import com.service.dao.enums.MemberJAType;
import com.service.dao.enums.MerchantOrderEnums;
import com.service.dao.enums.StatementTypeEnum;
import com.service.dao.mapper.MerchantOrderMapper;
import com.service.dao.model.*;
import com.service.dao.po.MemberCache;
import com.service.utility.common.MathUtil;
import com.service.utility.common.UUIDUtil;
import com.service.utility.modelmapper.ModelMapperUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;


@Slf4j
@Service("merchantOrderService")
public class MerchantOrderServiceImpl extends ServiceImpl<MerchantOrderMapper, MerchantOrder> implements MerchantOrderService {

    @Autowired
    private SessionUtil sessionUtil;
    @Autowired
    private ConfigService configService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private MerchantService merchantService;
    @Autowired
    private MemberStoreService storeService;
    @Autowired
    private MemberJAService memberJaService;
    @Autowired
    private CoinsPriceService coinsPriceService;
    @Autowired
    private MemberCacheService memberCacheService;
    @Autowired
    private MerchantCommodityService commodityService;
    @Autowired
    private MerchantCreditLineService creditLineService;
    @Autowired
    private MerchantOrderDetailService orderDetailService;
//    @Autowired
//    private WriteOffOrderSendHandler writeOffOrderSendHandler;
    @Autowired
    private MerchantCommodityPriceService commodityPriceService;
    @Autowired
    private MemberStarGradeConfigService starGradeConfigService;

    private BigDecimal ONE_HUNDRED = new BigDecimal(100);

    @Value("${merchantCommodityOrderPrefix}")
    private String merchantCommodityOrderPrefix;

    @Override
    public CommonPageRep<MerchantOrderPagRep> page(MerchantOrderPageReq pageRequest) {

        LoginUserSession session = sessionUtil.getUser();
        Merchant merchant = merchantService.getByMemberId(session.getUserId());
        if (ObjectUtils.isEmpty(merchant)) {
            throw new CommonException("暂未开通商家服务", "merchant_exception_not_service");
        }

        Page<MerchantOrder> page = PageHelper.startPage(pageRequest.getPageNo(), pageRequest.getPageSize());

        list(new LambdaQueryWrapper<MerchantOrder>()
                .eq(MerchantOrder::getMerchantId, merchant.getId())
                .eq(MerchantOrder::getOrderStatus, pageRequest.getOrderStatus())
                .orderByDesc(MerchantOrder::getCreateTime));

        return CommonPageRep.buildResultRep(page, page.getResult().stream()
                .map(item -> ModelMapperUtil.map(item, MerchantOrderPagRep.class))
                .collect(Collectors.toList()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(MerchantOrderCreateReq createReq, Member member) {
        LoginUserSession session = sessionUtil.getUser();
        Merchant merchant = merchantService.getByMemberId(session.getUserId());
        if (ObjectUtils.isEmpty(merchant)) {
            throw new CommonException("暂未开通商家服务", "merchant_exception_not_service");
        }

//        Map<String, SysConfig> configMap = configService.getConfigMapByGroup(ConfigConstants.Group.merchant.getCode());
//        SysConfig config = configMap.get(ConfigConstants.MERCHANT_CREDIT_LINE_COINS);

//        CoinsEnum concessionCoins = CoinsEnum.valueOf(Integer.parseInt(config.getConfValue()));
//        CoinsEnum rebateCoins = CoinsEnum.valueOf(Integer.parseInt(configMap.get(ConfigConstants.OFFLINE_ORDER_REBATE_COINS).getConfValue()));
//        CoinsEnum giftCoins = CoinsEnum.valueOf(Integer.parseInt(configMap.get(ConfigConstants.OFFLINE_ORDER_GIFT_MEMBER_COINS).getConfValue()));
        MerchantOrder merchantOrder = MerchantOrder.builder()
//                .concessionCoins(concessionCoins.getName())
                .coins(createReq.getCoins().getName())
//                .rebateCoins(rebateCoins.getName())
//                .giftCoins(giftCoins.getName())
                .memberId(member.getId())
                .memberLoginAccount(member.getUserAccount())
                .orderNo(buildMerchantCommodityOrderNo())
                .merchantId(merchant.getId())
                .payMoney(createReq.getPayMoney())
                .giftNum(BigDecimal.ZERO)
                .rebateNum(BigDecimal.ZERO)
                .createTime(new Date())
                .orderStatus(MerchantOrderEnums.Status.not_write_off.getCode())
                .build();

        save(merchantOrder);

        AtomicReference<BigDecimal> totalMoney = new AtomicReference<>(BigDecimal.ZERO);

        List<MerchantOrderDetail> orderDetails = createReq.getCommodityList().stream().map(item -> {
            MerchantOrderDetail detailItem = MerchantOrderDetail.builder().build();
            detailItem.setOrderId(merchantOrder.getId());
            detailItem.setBuyNum(item.getBuyNum());

            MerchantCommodity commodity = commodityService.getByCommodityNoAndStatus(item.getCommodityNo());
            detailItem.setCommodityId(commodity.getId());
            detailItem.setCommodityName(commodity.getCommodityName());

            Map<String, BigDecimal> priceMap = commodityPriceService.getCommodityPrice2CoinKeyMap(commodity.getId());

            BigDecimal commodityTotalMoney = priceMap.get(merchantOrder.getCoins()).multiply(BigDecimal.valueOf(item.getBuyNum()));
            //订单金额
            totalMoney.accumulateAndGet(commodityTotalMoney, BigDecimal::add);

            detailItem.setPrice(priceMap.get(merchantOrder.getCoins()));
            detailItem.setCoins(merchantOrder.getCoins());

            detailItem.setCreateTime(merchantOrder.getCreateTime());

            return detailItem;
        }).collect(Collectors.toList());

        orderDetailService.saveBatch(orderDetails);


//        config = configMap.get(ConfigConstants.OFFLINE_ORDER_CONCESSION_RATIO);


        //汇率
        BigDecimal exchangeRate = BigDecimal.ONE;
        if (StringUtils.equals(merchantOrder.getCoins(), CoinsEnum.CNY.getName())) {
            exchangeRate = coinsPriceService.getLastCoinPriceByCoinId(CoinsEnum.USDT.getCode()).getCoinPriceRmb();
        }
        BigDecimal totalMoneyUSDT = MathUtil.div(totalMoney.get(), exchangeRate, 8);
//        BigDecimal concessionRatio = getRatio(config);
//        BigDecimal totalConcession = concessionRatio.multiply(totalMoney.get());

        Map<String, BigDecimal> priceMap = coinsPriceService.getStandardPrice2Map(true);
//        BigDecimal concessionCoinPrice = getConcessionCoinPrice(configMap);
//        BigDecimal actualPrice = priceMap.get(merchantOrder.getConcessionCoins());
//        BigDecimal totalConcessionUsdt = concessionRatio.multiply(totalMoneyUSDT);
//        BigDecimal totalConcessionNum = MathUtil.div(totalConcessionUsdt, MathUtil.max(actualPrice, concessionCoinPrice), 8);

//        config = configMap.get(ConfigConstants.OFFLINE_ORDER_RELEASE_COINS_EXCHANGE_RATE);
//        SysConfig rebateRatio = configMap.get(ConfigConstants.OFFLINE_ORDER_REBATE_RATIO);

        updateById(MerchantOrder.builder()
                .id(merchantOrder.getId())
                .totalMoney(totalMoney.get())
                .totalMoneyUsdt(totalMoneyUSDT)
//                .totalConcession(totalConcession)
//                .totalConcessionNum(totalConcessionNum)
//                .totalConcessionUsdt(totalConcessionUsdt)
//                .giftNum(MathUtil.parseBigDecimal(config.getConfValue()).multiply(totalMoneyUSDT))
//                .rebateNum(MathUtil.parseBigDecimal(config.getConfValue()).multiply(totalMoneyUSDT).multiply(getRatio(rebateRatio)))
                .build());


//        writeOffOrderSendHandler.sendWriteOffOrder(WriteOffOrder.builder()
//                .memberId(member.getId()).orderId(merchantOrder.getId())
//                .merchantMemberId(session.getUserId())
//                .payMoney(merchantOrder.getPayMoney()).build());
    }

    private BigDecimal getConcessionCoinPrice(Map<String, SysConfig> configMap) {
        SysConfig config = configMap.get(ConfigConstants.OFFLINE_ORDER_CONCESSION_COIN_PRICE);
        return ObjectUtils.isEmpty(config) ? BigDecimal.ZERO : MathUtil.parseBigDecimal(config.getConfValue());
    }

    private BigDecimal getRatio(SysConfig config) {
        return MathUtil.div(MathUtil.parseBigDecimal(config.getConfValue()), ONE_HUNDRED, 8);
    }


    public String buildMerchantCommodityOrderNo() {
        String no = merchantCommodityOrderPrefix + DateFormatUtils.format(new Date(), "yyyyMMdd") + UUIDUtil.randomUUID10().substring(0, 8);
        MerchantOrder commodityOrder = getByOrderNo(no);
        if (!ObjectUtils.isEmpty(commodityOrder)) {
            no = buildMerchantCommodityOrderNo();
        }
        return no;
    }

    @Override
    public MerchantOrder getByOrderNo(String orderNo) {
        return getOne(new LambdaQueryWrapper<MerchantOrder>().eq(MerchantOrder::getOrderNo, orderNo));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void writeOff(MerchantOrder order, Integer memberId, MemberStore memberExdfStore, MerchantCreditLine merchantCreditLine) {
        Merchant merchant = merchantService.getByMemberId(memberId);
        if (ObjectUtils.isEmpty(merchant)) {
            throw new CommonException("暂未开通商家服务", "merchant_exception_not_service");
        }

        //商家让利
        log.info("商家<no:{},name:{}>让利:{}{}", merchant.getMerchantNo(), merchant.getMerchantName(),
                order.getTotalConcessionNum(), order.getConcessionCoins());
        BigDecimal notConcessionNum = order.getTotalConcessionNum();

        if (!MathUtil.isLEZero(memberExdfStore.getNum())) {
            notConcessionNum = notConcessionNum.subtract(memberExdfStore.getNum());
            BigDecimal useNumByStore = order.getTotalConcessionNum();
            if (!MathUtil.isLEZero(notConcessionNum)) {
                useNumByStore = memberExdfStore.getNum();
            }
            offlineOrderConcessionByMemberStore(memberId, order, merchant, useNumByStore);
        }

        if (!MathUtil.isLEZero(notConcessionNum)) {
            offlineOrderConcessionByCreditLine(order, merchant, notConcessionNum);
        }

        //商家返利
        log.info("商家<no:{},name:{}>返利:{}{}", merchant.getMerchantNo(), merchant.getMerchantName(),
                order.getRebateNum(), order.getRebateCoins());
        offlineOrderRebate(memberId, order, merchant);

        //发放积分
        MemberCache memberCache = memberCacheService.getMemberCache(order.getMemberId());
        MemberStarGradeConfig starGradeConfig = starGradeConfigService.getById(memberCache.getStarGrade());
        List<Integer> jaTypeList = Lists.newArrayList();
        jaTypeList.add(MemberJAType.OFFLINE_GIFT.getCode());
        BigDecimal totalStatement = memberJaService.getSomedayTotalStatement(order.getMemberId(), order.getGiftCoins(), new Date(), jaTypeList);

        log.info("会员<星级:{},每天最大赠送数量:{}>此时已赠送总计:{},订单<{}>赠送数量:{}", memberCache.getStarGrade(),
                starGradeConfig.getReceiveNumDaily(), totalStatement, order.getOrderNo(), order.getGiftNum());

        updateById(MerchantOrder.builder()
                .id(order.getId())
                .orderStatus(MerchantOrderEnums.Status.write_off.getCode())
                .build());

        if (totalStatement.compareTo(starGradeConfig.getReceiveNumDaily()) >= 0) {
            return;
        }

        BigDecimal giftNum = MathUtil.subAndZero(starGradeConfig.getReceiveNumDaily(), totalStatement);
        if (giftNum.compareTo(order.getGiftNum()) > 0) {
            giftNum = order.getGiftNum();
        }
        offlineOrderGift(order, merchant, giftNum);
        memberService.checkAndUpdateMemberStarGrade(memberCache);
    }

    public void offlineOrderConcessionByMemberStore(Integer memberId, MerchantOrder order, Merchant merchant, BigDecimal changeNum) {
        CoinsEnum coinsEnum = CoinsEnum.valueOf(order.getConcessionCoins());

        MemberStoreChange storeChange = MemberStoreChange.builder()
                .changeNum(order.getTotalConcessionNum()).memberId(memberId)
                .coinsId(coinsEnum.getCode())
                .coinsName(coinsEnum.getName())
                .orderId(order.getId())
                .jaType(MemberJAType.WRITE_OFF)
                .remark("线下商家<" + merchant.getMerchantName() + ">核销订单让利")
                .build();

        storeService.changeMemberStore(storeChange);
    }

    public void offlineOrderConcessionByCreditLine(MerchantOrder order, Merchant merchant, BigDecimal changeNum) {
        CoinsEnum coinsEnum = CoinsEnum.valueOf(order.getConcessionCoins());

        MerchantCreditLineChange storeChange = MerchantCreditLineChange.builder()
                .merchantId(order.getMerchantId())
                .changeNum(order.getTotalConcessionNum())
                .coinsId(coinsEnum.getCode())
                .coinsName(coinsEnum.getName())
                .orderId(order.getId())
                .statementType(StatementTypeEnum.WRITE_OFF)
                .remark("线下商家<" + merchant.getMerchantName() + ">核销订单让利")
                .build();

        creditLineService.updateCreditLineBalance(storeChange);
    }

    public void offlineOrderGift(MerchantOrder order, Merchant merchant, BigDecimal giftNum) {
        CoinsEnum coinsEnum = CoinsEnum.valueOf(order.getGiftCoins());
        MemberStoreChange storeChange = MemberStoreChange.builder()
                .changeNum(giftNum).memberId(order.getMemberId())
                .coinsId(coinsEnum.getCode())
                .coinsName(coinsEnum.getName())
                .orderId(order.getId())
                .jaType(MemberJAType.OFFLINE_GIFT)
                .remark("线下商家<" + merchant.getMerchantName() + ">核销订单赠送")
                .build();

        storeService.changeMemberStore(storeChange);
    }

    public void offlineOrderRebate(Integer memberId, MerchantOrder order, Merchant merchant) {
        CoinsEnum coinsEnum = CoinsEnum.valueOf(order.getRebateCoins());

        MemberStoreChange storeChange = MemberStoreChange.builder()
                .changeNum(order.getRebateNum()).memberId(memberId)
                .coinsId(coinsEnum.getCode())
                .coinsName(coinsEnum.getName())
                .orderId(order.getId())
                .jaType(MemberJAType.OFFLINE_REBATE)
                .remark("线下商家<" + merchant.getMerchantName() + ">核销订单返利")
                .build();

        storeService.changeMemberStore(storeChange);
    }

    @Override
    public BigDecimal getOfflineTotalConsumption(List<Integer> memberIdList) {
        return baseMapper.sumTotalMoneyUsdtByMemberIdList(memberIdList);
    }

    @Override
    public BigDecimal getOfflineTotalConcessionUsdt(List<Integer> memberIdList) {
        return baseMapper.sumTotalConcessionUsdtByMemberIdList(memberIdList);
    }
}
